package aceim.app.service;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import aceim.api.dataentity.Buddy;
import aceim.api.dataentity.BuddyGroup;
import aceim.api.dataentity.ConnectionState;
import aceim.api.dataentity.FileMessage;
import aceim.api.dataentity.FileProgress;
import aceim.api.dataentity.InputFormFeature;
import aceim.api.dataentity.ItemAction;
import aceim.api.dataentity.Message;
import aceim.api.dataentity.MessageAckState;
import aceim.api.dataentity.MultiChatRoom;
import aceim.api.dataentity.OnlineInfo;
import aceim.api.dataentity.PersonalInfo;
import aceim.api.dataentity.ProtocolOption;
import aceim.api.dataentity.ServiceMessage;
import aceim.api.dataentity.TextMessage;
import aceim.api.service.ApiConstants;
import aceim.api.service.ICoreProtocolCallback;
import aceim.api.service.IProtocolService;
import aceim.api.service.ProtocolException;
import aceim.api.service.ProtocolException.Cause;
import aceim.api.utils.Logger;
import aceim.api.utils.Logger.LoggerLevel;
import aceim.api.utils.ServiceHelper;
import aceim.app.AceImException;
import aceim.app.AceImException.AceImExceptionReason;
import aceim.app.Constants;
import aceim.app.Constants.OptionKey;
import aceim.app.R;
import aceim.app.dataentity.Account;
import aceim.app.dataentity.AccountOptionKeys;
import aceim.app.dataentity.AccountService;
import aceim.app.dataentity.GlobalOptionKeys;
import aceim.app.dataentity.ProtocolResources;
import aceim.app.service.ProtocolServicesManager.ProtocolListener;
import aceim.app.utils.DataStorage;
import aceim.app.utils.ImportAndExport;
import aceim.app.utils.LocationSender;
import aceim.app.utils.Notificator;
import aceim.app.utils.Notificator.LEDNotificationMode;
import aceim.app.utils.Notificator.SoundNotificationMode;
import aceim.app.utils.Notificator.StatusBarNotificationMode;
import aceim.app.utils.OptionsReceiver;
import aceim.app.utils.OptionsReceiver.OnOptionChangedListener;
import aceim.app.utils.ViewUtils;
import aceim.app.utils.history.HistorySaver;
import aceim.app.utils.history.impl.JsonHistorySaver;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.PowerManager;
import android.os.RemoteException;
import android.support.v4.content.LocalBroadcastManager;
import android.text.TextUtils;
import com.androidquery.callback.BitmapAjaxCallback;
public class CoreService extends Service {
private static byte sReconnectionAttempts;
private WifiManager.WifiLock wifiLock = null;
private PowerManager.WakeLock powerLock = null;
private final ServiceHelper mServiceHelper = new ServiceHelper(this);
private ProtocolServicesManager mProtocolServiceManager;
private OptionsReceiver mOptionsReceiver;
private Notificator mNotificator;
private LocationSender mLocationSender;
private final List<AccountService> mAccounts = new ArrayList<AccountService>();
private volatile boolean mProtocolsReady = false;
private volatile boolean mAccountsReady = false;
private volatile boolean mExiting = false;
private DataStorage mStorage;
private HistorySaver mHistorySaver;
private IUserInterface mUserInterface;
// private boolean uiVisible = true;
private Bundle mSavedInstanceState = null;
private final Handler mHandler = new Handler();
private final ScheduledExecutorService mScheduledExecutor = Executors.newScheduledThreadPool(1);
private final Runnable mSaveAccountsRunnable = new Runnable() {
@Override
public void run() {
mStorage.saveAccounts(mAccounts);
}
};
private final ProtocolListener mProtocolListener = new ProtocolListener() {
@Override
public void onAction(ProtocolService protocol, ItemAction action) {
if (!mAccountsReady) {
return;
}
switch (action) {
case JOINED:
for (AccountService as : mAccounts) {
if (as != null && as.getProtocolService().getProtocolServicePackageName().equals(protocol.getProtocolServicePackageName())) {
try {
Account a = as.getAccount();
as.getProtocolService().getProtocol().addAccount(a.getServiceId(), a.getProtocolUid());
mProtocolCallback.connectionStateChanged(as.getAccount().getServiceId(), ConnectionState.DISCONNECTED, -1);
} catch (RemoteException e) {
Logger.log(e);
}
}
}
break;
case LEFT:
// TODO notification
break;
default:
if (mUserInterface != null) {
try {
mUserInterface.terminate();
exitService(true);
} catch (RemoteException e) {
Logger.log(e);
}
}
break;
}
}
};
private final OnOptionChangedListener mOptionsReceiverListener = new OnOptionChangedListener() {
@Override
public void onOptionChanged(OptionKey key, String value, byte serviceId) {
Account account = null;
if (serviceId > -1) {
AccountService acs = mAccounts.get(serviceId);
if (acs != null) {
account = acs.getAccount();
}
}
onOptionChangedInternal(key, value, account);
}
};
private final Runnable mInitProtocolsRunnable = new Runnable() {
@Override
public void run() {
Logger.log("Init protocols", LoggerLevel.VERBOSE);
mProtocolServiceManager.initProtocolServices();
mProtocolsReady = true;
}
};
private final Runnable mInitAccountsRunnable = new Runnable() {
@Override
public void run() {
initAccounts();
initApplicationOptions();
initAutoconnection();
}
};
@Override
public IBinder onBind(Intent intent) {
// uiVisible = true;
return mInterfaceBinder;
}
private void initAutoconnection() {
boolean autoconnect = getSharedPreferences(Constants.SHARED_PREFERENCES_GLOBAL, 0).getBoolean(GlobalOptionKeys.AUTOCONNECT.name(), false);
for (AccountService a : mAccounts) {
if (a == null || !a.getAccount().isEnabled()) {
continue;
}
// if service connection was interrupted or auto connection is
// toggled, do the connection
if (a.getAccount().getConnectionState() != ConnectionState.DISCONNECTED || autoconnect) {
a.getAccount().setConnectionState(ConnectionState.DISCONNECTED);
connectInternal(a.getAccount().getServiceId());
}
}
}
@Override
public boolean onUnbind(Intent intent) {
mUserInterface = null;
// uiVisible = false;
return false;
}
@Override
public void onCreate() {
super.onCreate();
initLocals();
// To avoid deadlocks between several service binders (UI & protocols),
// we should distinguish their initializing from main lifecycle methods
ThreadFactory tf = Executors.defaultThreadFactory();
tf.newThread(mInitProtocolsRunnable).start();
tf.newThread(mInitAccountsRunnable).start();
}
@Override
public void onDestroy() {
try {
cleanupResources();
} catch (Exception e) {
}
super.onDestroy();
}
@Override
public void onStart(Intent intent, int startId) {
handleCommand(intent);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
handleCommand(intent);
return START_NOT_STICKY;
}
private void handleCommand(final Intent intent) {
if (intent == null || !intent.hasExtra(Constants.INTENT_EXTRA_CLASS_NAME)) {
return;
}
Executors.defaultThreadFactory().newThread(new Runnable() {
@Override
public void run() {
String classNameExtra = intent.getStringExtra(Constants.INTENT_EXTRA_CLASS_NAME);
if (classNameExtra.equals(FileMessage.class.getName()) || classNameExtra.equals(ServiceMessage.class.getName())) {
Message m = intent.getParcelableExtra(Constants.INTENT_EXTRA_MESSAGE);
String accept = intent.getData().getHost();
boolean doAcceptFile = accept.equals(getBaseContext().getString(android.R.string.ok));
respondMessageInternal(m, doAcceptFile);
}
}
}).start();
}
private void initLocals() {
PowerManager powerManager = (PowerManager) getApplicationContext().getSystemService(POWER_SERVICE);
powerLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "AceIM Power Lock");
powerLock.acquire();
WifiManager wlanManager = (WifiManager) getApplicationContext().getSystemService(WIFI_SERVICE);
if (wlanManager != null) {
wifiLock = wlanManager.createWifiLock(WifiManager.WIFI_MODE_FULL, "AceIM Wireless Lock");
wifiLock.acquire();
}
sReconnectionAttempts = (byte) getSharedPreferences(Constants.SHARED_PREFERENCES_GLOBAL, 0).getInt(GlobalOptionKeys.RECONNECTION_ATTEMPTS.name(), Integer.parseInt(getString(R.string.default_reconnection_attempts)));
mServiceHelper.doStartForeground();
mOptionsReceiver = new OptionsReceiver(mOptionsReceiverListener);
LocalBroadcastManager.getInstance(getBaseContext()).registerReceiver(mOptionsReceiver, new IntentFilter(Constants.INTENT_ACTION_OPTION));
mNotificator = new Notificator(getApplicationContext());
mNotificator.setVolumeLevel(getSharedPreferences(Constants.SHARED_PREFERENCES_GLOBAL, 0).getInt(GlobalOptionKeys.SOUND_NOTIFICATION_VOLUME.name(), 100) / 100f);
mStorage = new DataStorage(getApplicationContext());
mHistorySaver = new JsonHistorySaver(getApplicationContext());
mProtocolServiceManager = new ProtocolServicesManager(getApplicationContext(), mProtocolCallback, mProtocolListener);
mLocationSender = new LocationSender(this);
}
private void initAccounts() {
Logger.log("Init accounts... waiting for protocols", LoggerLevel.VERBOSE);
while (!mProtocolsReady) {
try {
Thread.sleep(250);
} catch (InterruptedException e) {
}
}
Logger.log("Protocols got", LoggerLevel.VERBOSE);
List<Account> accounts = mStorage.getAccounts();
for (Account account : accounts) {
if (account == null || account.getProtocolUid() == null)
continue;
AccountService acs = initAccount(account);
boolean disabled = getSharedPreferences(account.getAccountId(), ServiceUtils.getAccessMode()).getBoolean(AccountOptionKeys.DISABLED.name(), false);
account.setEnabled(!disabled);
mAccounts.add(acs);
// test(account);
}
mAccountsReady = true;
}
private AccountService initAccount(Account account) {
Logger.log("Init " + account.getAccountId(), LoggerLevel.VERBOSE);
AccountService acc = ServiceUtils.makeAccount(account, mProtocolServiceManager);
if (acc.getProtocolService() == null && !getBaseContext().getSharedPreferences(account.getAccountId(), ServiceUtils.getAccessMode()).getBoolean(AccountOptionKeys.DISABLED.getStringKey(), false)) {
account.setEnabled(false);
getBaseContext().getSharedPreferences(account.getAccountId(), ServiceUtils.getAccessMode()).edit().putBoolean(AccountOptionKeys.DISABLED.getStringKey(), true).commit();
mNotificator.processException(new AceImException(AceImExceptionReason.NO_PROTOCOL_FOUND), account);
}
acc.setConnectionAttempts(sReconnectionAttempts);
return acc;
}
private void initApplicationOptions() {
Context c = getBaseContext();
SharedPreferences options = c.getSharedPreferences(Constants.SHARED_PREFERENCES_GLOBAL, 0);
LEDNotificationMode ledMode = LEDNotificationMode.valueOf(options.getString(GlobalOptionKeys.LED_BLINKER.name(), c.getString(R.string.default_led_blinker)));
mNotificator.setLEDNotificationMode(ledMode);
SoundNotificationMode soundMode = SoundNotificationMode.valueOf(options.getString(GlobalOptionKeys.SOUND_NOTIFICATION_TYPE.name(), c.getString(R.string.default_sound_notification)));
mNotificator.setSoundMode(soundMode);
StatusBarNotificationMode statusbarMode = StatusBarNotificationMode.valueOf(options.getString(GlobalOptionKeys.STATUSBAR_NOTIFICATION_TYPE.name(), c.getString(R.string.default_statusbar_notification)));
mNotificator.setStatusBarMode(statusbarMode);
boolean messageSoundOnly = options.getBoolean(GlobalOptionKeys.MESSAGE_SOUND_ONLY.name(), Boolean.parseBoolean(c.getString(R.string.default_message_sound_only)));
mNotificator.setMessageSoundOnly(messageSoundOnly);
mNotificator.onAccountStateChanged(mAccounts);
}
private void onOptionChangedInternal(OptionKey key, String value, Account account) {
if (key instanceof GlobalOptionKeys) {
GlobalOptionKeys k = (GlobalOptionKeys) key;
switch (k) {
case LED_BLINKER:
LEDNotificationMode ledMode = LEDNotificationMode.valueOf(value);
mNotificator.setLEDNotificationMode(ledMode);
break;
case SOUND_NOTIFICATION_TYPE:
SoundNotificationMode soundMode = SoundNotificationMode.valueOf(value);
mNotificator.setSoundMode(soundMode);
break;
case SOUND_NOTIFICATION_VOLUME:
int level = Integer.valueOf(value);
mNotificator.setVolumeLevel(level / 100f);
break;
case RECONNECTION_ATTEMPTS:
sReconnectionAttempts = Byte.parseByte(value);
break;
case STATUSBAR_NOTIFICATION_TYPE:
StatusBarNotificationMode barMode = StatusBarNotificationMode.valueOf(value);
mNotificator.setStatusBarMode(barMode);
mNotificator.onAccountStateChanged(mAccounts);
case LOG_TO_FILE:
boolean logToFile = Boolean.parseBoolean(value);
Logger.logToFile = logToFile;
for (ProtocolService protocol : mProtocolServiceManager.getProtocolsList()) {
try {
protocol.getProtocol().logToFile(logToFile);
} catch (RemoteException e) {
Logger.log(e);
}
}
break;
default:
break;
}
}
}
private final ICoreService.Stub mInterfaceBinder = new ICoreService.Stub() {
@Override
public long sendMessage(Message message) throws RemoteException {
AccountService as = mAccounts.get(message.getServiceId());
if (as == null || !as.getAccount().isEnabled() || as.getAccount().getConnectionState() != ConnectionState.CONNECTED) {
return 0;
}
Logger.log("UI sends message ", LoggerLevel.VERBOSE);
Buddy buddy = getBuddy(message.getServiceId(), message.getContactUid());
if (buddy.getOnlineInfo().getFeatures().containsKey(ApiConstants.FEATURE_BUDDY_RESOURCE)) {
message.setContactDetail(buddy.getOnlineInfo().getFeatures().getString(ApiConstants.FEATURE_BUDDY_RESOURCE));
}
long messageId = as.getProtocolService().getProtocol().sendMessage(message);
Logger.log("UI file sending got message ID #" + messageId);
message.setMessageId(messageId);
if (message instanceof FileMessage) {
FileProgress progress = new FileProgress(message.getServiceId(), messageId, ((FileMessage) message).getFiles().get(0).getFilename(), 0, 0, false, message.getContactUid(), null);
if (mUserInterface != null) {
mUserInterface.onFileProgress(progress);
}
}
mHistorySaver.saveMessage(buddy, message);
return messageId;
}
@Override
public Account createAccount(String protocolServiceClassName, List<ProtocolOption> options) throws RemoteException {
Logger.log("UI creates account " + protocolServiceClassName, LoggerLevel.VERBOSE);
String protocolName = mProtocolServiceManager.getProtocolServiceByName(protocolServiceClassName).getProtocol().getProtocolName();
if (findAccountServiceByProtocolUidAndProtocolName(options.get(0).getValue(), protocolName) != null) {
ViewUtils.showAlertToast(getBaseContext(), android.R.drawable.ic_menu_info_details, R.string.simple_placeholder, options.get(0).getValue());
return null;
} else {
Account account = new Account((byte) mAccounts.size(), options.get(0).getValue(), protocolName, protocolServiceClassName);
mStorage.saveAccount(account, options, true);
mAccounts.add(initAccount(account));
return account;
}
}
@Override
public void deleteAccount(Account account) throws RemoteException {
AccountService as = mAccounts.get(account.getServiceId());
if (as == null) {
return;
}
if (as.getProtocolService() != null && as.getAccount().getConnectionState() != ConnectionState.DISCONNECTED) {
as.getProtocolService().getProtocol().disconnect(account.getServiceId());
}
mStorage.removeAccount(account);
mNotificator.removeAccountIcon(account);
mHistorySaver.removeAccount(account);
ViewUtils.removeAccountIcons(account, getBaseContext());
mAccounts.set(account.getServiceId(), null);
}
@Override
public void editAccount(Account account, List<ProtocolOption> options, String protocolServicePackageName) throws RemoteException {
AccountService as = mAccounts.get(account.getServiceId());
if (as == null) {
return;
}
Logger.log("UI edits account " + account.getAccountId(), LoggerLevel.VERBOSE);
if (account.getConnectionState() == ConnectionState.CONNECTED) {
mAccounts.get(account.getServiceId()).getProtocolService().getProtocol().disconnect(account.getServiceId());
}
if (protocolServicePackageName != null && !protocolServicePackageName.equals(account.getProtocolServicePackageName())) {
Logger.log("Setting protocol class " + protocolServicePackageName + " to " + account.getAccountId(), LoggerLevel.VERBOSE);
account = new Account(account.getServiceId(), account.getProtocolUid(), account.getProtocolName(), protocolServicePackageName);
as = initAccount(account);
mAccounts.set(account.getServiceId(), as);
} else {
as.getAccount().merge(account);
}
mStorage.saveAccount(account, options, false);
mNotificator.onAccountStateChanged(mAccounts);
if (mUserInterface != null) {
mUserInterface.onAccountUpdated(account, ItemAction.MODIFIED);
}
}
@Override
public List<Account> getAccounts(boolean disabledToo) throws RemoteException {
Logger.log("UI requests accounts", LoggerLevel.VERBOSE);
while (!mAccountsReady) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
}
List<Account> accounts = new ArrayList<Account>(mAccounts.size());
for (AccountService a : mAccounts) {
if (a == null || a.getAccount() == null || (!disabledToo && !a.getAccount().isEnabled())) {
continue;
}
accounts.add(a.getAccount());
}
return accounts;
}
@Override
public Buddy getBuddy(byte serviceId, String buddyProtocolUid) throws RemoteException {
Logger.log("UI requests buddy " + buddyProtocolUid, LoggerLevel.VERBOSE);
return getAccount(serviceId).getBuddyByProtocolUid(buddyProtocolUid);
}
@Override
public List<Buddy> getBuddies(byte serviceId, List<String> buddyProtocolUid) throws RemoteException {
// TODO Auto-generated method stub
return null;
}
@Override
public Account getAccount(byte serviceId) throws RemoteException {
Logger.log("UI requests account " + serviceId, LoggerLevel.VERBOSE);
return mAccounts.get(serviceId).getAccount();
}
@Override
public void connect(byte serviceId) throws RemoteException {
Logger.log("UI requests connect " + serviceId, LoggerLevel.VERBOSE);
connectInternal(serviceId);
}
@Override
public void connectAll() throws RemoteException {
for (byte i = 0; i < mAccounts.size(); i++) {
connectInternal(i);
}
}
@Override
public void disconnectAll() throws RemoteException {
disconnectAllInternal(false);
}
@Override
public void disconnect(byte serviceId) throws RemoteException {
Logger.log("UI requests disconnect " + serviceId, LoggerLevel.VERBOSE);
disconnectInternal(serviceId);
}
@Override
public void resetUnread(Buddy buddy) throws RemoteException {
AccountService as = mAccounts.get(buddy.getServiceId());
if (as == null || !as.getAccount().isEnabled()) {
return;
}
Logger.log("UI requests unreads reset for " + buddy, LoggerLevel.VERBOSE);
Account a = as.getAccount();
Buddy b = a.getBuddyByProtocolUid(buddy.getProtocolUid());
if (b != buddy && b.getUnread() > 0) {
b.setUnread((byte) 0);
}
mNotificator.removeMessageNotification(b, mAccounts);
}
@Override
public void addBuddy(Buddy buddy) throws RemoteException {
AccountService as = mAccounts.get(buddy.getServiceId());
if (as == null || !as.getAccount().isEnabled() || as.getAccount().getConnectionState() != ConnectionState.CONNECTED) {
return;
}
as.getProtocolService().getProtocol().buddyAction(ItemAction.ADDED, buddy);
}
@Override
public void removeBuddy(Buddy buddy) throws RemoteException {
AccountService as = mAccounts.get(buddy.getServiceId());
if (as == null || !as.getAccount().isEnabled() || as.getAccount().getConnectionState() != ConnectionState.CONNECTED) {
return;
}
if (buddy instanceof MultiChatRoom) {
as.getProtocolService().getProtocol().leaveChatRoom(buddy.getServiceId(), buddy.getProtocolUid());
as.getAccount().removeBuddyByUid(buddy.getProtocolUid());
onContactListUpdated(as.getAccount());
} else {
as.getProtocolService().getProtocol().buddyAction(ItemAction.DELETED, buddy);
}
}
@Override
public void renameBuddy(Buddy buddy) throws RemoteException {
AccountService as = mAccounts.get(buddy.getServiceId());
if (as == null || !as.getAccount().isEnabled() || as.getAccount().getConnectionState() != ConnectionState.CONNECTED) {
return;
}
as.getProtocolService().getProtocol().buddyAction(ItemAction.MODIFIED, buddy);
}
@Override
public void moveBuddy(Buddy buddy) throws RemoteException {
AccountService as = mAccounts.get(buddy.getServiceId());
if (as == null || !as.getAccount().isEnabled() || as.getAccount().getConnectionState() != ConnectionState.CONNECTED) {
return;
}
as.getProtocolService().getProtocol().buddyAction(ItemAction.MODIFIED, buddy);
}
@Override
public void addGroup(BuddyGroup group) throws RemoteException {
AccountService as = mAccounts.get(group.getServiceId());
if (as == null || !as.getAccount().isEnabled() || as.getAccount().getConnectionState() != ConnectionState.CONNECTED) {
return;
}
as.getProtocolService().getProtocol().buddyGroupAction(ItemAction.ADDED, group);
}
@Override
public void removeGroup(BuddyGroup group) throws RemoteException {
AccountService as = mAccounts.get(group.getServiceId());
if (as == null || !as.getAccount().isEnabled() || as.getAccount().getConnectionState() != ConnectionState.CONNECTED) {
return;
}
as.getProtocolService().getProtocol().buddyGroupAction(ItemAction.DELETED, group);
}
@Override
public void renameGroup(BuddyGroup group) throws RemoteException {
AccountService as = mAccounts.get(group.getServiceId());
if (as == null || !as.getAccount().isEnabled() || as.getAccount().getConnectionState() != ConnectionState.CONNECTED) {
return;
}
as.getProtocolService().getProtocol().buddyGroupAction(ItemAction.MODIFIED, group);
}
@Override
public void setGroupCollapsed(byte serviceId, String groupId, boolean collapsed) throws RemoteException {
AccountService as = mAccounts.get(serviceId);
if (as == null) {
return;
}
as.getAccount().getBuddyGroupByGroupId(groupId).setCollapsed(collapsed);
mStorage.saveAccount(as.getAccount(), false);
}
@Override
public void requestBuddyShortInfo(byte serviceId, String uid) throws RemoteException {
AccountService as = mAccounts.get(serviceId);
if (as == null || !as.getAccount().isEnabled() || as.getAccount().getConnectionState() != ConnectionState.CONNECTED) {
return;
}
as.getProtocolService().getProtocol().requestFullInfo(serviceId, uid, true);
}
@Override
public void requestBuddyFullInfo(byte serviceId, String uid) throws RemoteException {
AccountService as = mAccounts.get(serviceId);
if (as == null || !as.getAccount().isEnabled() || as.getAccount().getConnectionState() != ConnectionState.CONNECTED) {
return;
}
as.getProtocolService().getProtocol().requestFullInfo(serviceId, uid, false);
}
@Override
public void respondMessage(Message msg, boolean accept) throws RemoteException {
respondMessageInternal(msg, accept);
}
@Override
public void cancelFileTransfer(byte serviceId, long messageId) throws RemoteException {
AccountService as = mAccounts.get(serviceId);
if (as == null || !as.getAccount().isEnabled() || as.getAccount().getConnectionState() != ConnectionState.CONNECTED) {
return;
}
as.getProtocolService().getProtocol().cancelFileFransfer(serviceId, messageId);
}
@Override
public void exit(boolean terminate) throws RemoteException {
exitService(terminate);
}
@Override
public void sendTyping(byte serviceId, String buddyUid) throws RemoteException {
AccountService as = mAccounts.get(serviceId);
if (as == null || !as.getAccount().isEnabled() || as.getAccount().getConnectionState() != ConnectionState.CONNECTED) {
return;
}
Logger.log("UI requests typing notification " + serviceId, LoggerLevel.VERBOSE);
as.getProtocolService().getProtocol().sendTypingNotification(serviceId, buddyUid);
}
@Override
public void editBuddy(Buddy buddy) throws RemoteException {
AccountService as = mAccounts.get(buddy.getServiceId());
if (as == null || !as.getAccount().isEnabled() || as.getAccount().getConnectionState() != ConnectionState.CONNECTED) {
return;
}
as.getProtocolService().getProtocol().buddyAction(ItemAction.MODIFIED, buddy);
}
@Override
public void leaveChat(byte serviceId, String chatId) throws RemoteException {
AccountService as = mAccounts.get(serviceId);
if (as == null || !as.getAccount().isEnabled() || as.getAccount().getConnectionState() != ConnectionState.CONNECTED) {
return;
}
as.getProtocolService().getProtocol().leaveChatRoom(serviceId, chatId);
}
@Override
public void joinChat(byte serviceId, String chatId) throws RemoteException {
AccountService as = mAccounts.get(serviceId);
Account a = as.getAccount();
if (as == null || !a.isEnabled() || a.getConnectionState() != ConnectionState.CONNECTED) {
return;
}
// The following actions are required in a case when user joins
// non-already-existing chat
Buddy b = a.getBuddyByProtocolUid(chatId);
if (b == null) {
b = new MultiChatRoom(chatId, a.getProtocolUid(), a.getProtocolName(), a.getServiceId());
a.addBuddyToList(b);
}
boolean loadIcons = getBaseContext().getSharedPreferences(a.getAccountId(), ServiceUtils.getAccessMode()).getBoolean(AccountOptionKeys.LOAD_ICONS.name(),
Boolean.parseBoolean(getBaseContext().getString(R.string.default_load_icons)));
as.getProtocolService().getProtocol().joinChatRoom(serviceId, chatId, loadIcons);
}
@Override
public void registerCallback(IUserInterface callback) throws RemoteException {
Logger.log("UI callback registering", LoggerLevel.VERBOSE);
mUserInterface = callback;
}
@Override
public List<ProtocolResources> getAllProtocolResources(boolean getProtocolInfo) throws RemoteException {
Logger.log("UI requests protocol resources", LoggerLevel.VERBOSE);
while (!mAccountsReady) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
}
List<ProtocolService> protocols = mProtocolServiceManager.getProtocolsList();
List<ProtocolResources> list = new ArrayList<ProtocolResources>(protocols.size());
for (ProtocolService p : protocols) {
list.add(p.getResources(getProtocolInfo));
}
return list;
}
@Override
public List<ProtocolOption> getProtocolOptions(String protocolServiceClass, byte serviceId) throws RemoteException {
Logger.log("UI requests protocol options for " + protocolServiceClass, LoggerLevel.VERBOSE);
while (!mProtocolsReady) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
}
ProtocolOption[] options = mProtocolServiceManager.getProtocolServiceByName(protocolServiceClass).getProtocol().getProtocolOptions();
if (serviceId > -1) {
Account account = mAccounts.get(serviceId).getAccount();
for (ProtocolOption o : options) {
o.setValue(mStorage.getProtocolOptionValue(o.getKey(), account));
}
}
return Arrays.asList(options);
}
@Override
public void notifyUnread(Message message, Buddy buddy) throws RemoteException {
Logger.log("UI requests message notification: " + message, LoggerLevel.VERBOSE);
Account a = mAccounts.get(message.getServiceId()).getAccount();
Buddy b = a.getBuddyByProtocolUid(buddy.getProtocolUid());
// If we run CoreService in a different process, then buddy
// instances are different, so we need to update one within
// CoreService.
if (b != buddy && b.getUnread() != buddy.getUnread()) {
b.setUnread(buddy.getUnread());
}
mNotificator.onMessage(message, a);
}
@Override
public List<Message> getLastMessages(Buddy buddy) throws RemoteException {
Logger.log("UI requests last messages for " + buddy, LoggerLevel.VERBOSE);
return mHistorySaver.getMessages(buddy);
}
@Override
public List<Message> getMessages(Buddy buddy, int startFrom, int maxMessagesToRead) throws RemoteException {
Logger.log("UI requests " + maxMessagesToRead + " messages for " + buddy + ", starting from " + startFrom, LoggerLevel.VERBOSE);
return mHistorySaver.getMessages(buddy, startFrom, maxMessagesToRead);
}
@Override
public boolean deleteMessagesHistory(Buddy buddy) throws RemoteException {
return mHistorySaver.deleteHistory(buddy);
}
@Override
public void saveInstanceState(Bundle bundle) throws RemoteException {
mSavedInstanceState = bundle;
}
@Override
public Bundle restoreInstanceState() throws RemoteException {
return mSavedInstanceState;
}
@Override
public void sendLocation(final Buddy buddy) throws RemoteException {
mHandler.post(new Runnable(){
@Override
public void run() {
mLocationSender.requestLocationForBuddy(buddy);
}
});
}
@Override
public void importAccounts(String password, FileProgress progress) throws RemoteException {
ImportAndExport.importData(progress, password, mUserInterface, CoreService.this);
}
@Override
public void exportAccounts(String password, FileProgress progress) throws RemoteException {
ImportAndExport.exportData(progress, password, mUserInterface, getBaseContext());
}
@Override
public void uploadAccountPhoto(byte serviceId, String filename) throws RemoteException {
AccountService as = mAccounts.get(serviceId);
if (as == null || !as.getAccount().isEnabled() || as.getAccount().getConnectionState() != ConnectionState.CONNECTED) {
return;
}
as.getProtocolService().getProtocol().uploadAccountPhoto(serviceId, filename);
}
@Override
public void removeAccountPhoto(byte serviceId) throws RemoteException {
AccountService as = mAccounts.get(serviceId);
if (as == null || !as.getAccount().isEnabled() || as.getAccount().getConnectionState() != ConnectionState.CONNECTED) {
return;
}
as.getProtocolService().getProtocol().removeAccountPhoto(serviceId);
ViewUtils.removeIcon(getBaseContext(), as.getAccount().getFilename());
if (mUserInterface != null) {
try {
mUserInterface.onAccountIcon(serviceId);
} catch (RemoteException e) {
Logger.log(e);
}
}
}
@Override
public void setFeature(String featureId, OnlineInfo info) throws RemoteException {
AccountService as = mAccounts.get(info.getServiceId());
if (as == null || !as.getAccount().isEnabled() || as.getAccount().getConnectionState() != ConnectionState.CONNECTED) {
return;
}
Account account = as.getAccount();
as.getProtocolService().getProtocol().setFeature(featureId, info);
if (info.getProtocolUid().equals(account.getProtocolUid())) {
OnlineInfo onlineInfo = account.getOnlineInfo();
if (onlineInfo != info) {
// TODO fix for other feature types
as.getAccount().getOnlineInfo().getFeatures().putByte(featureId, info.getFeatures().getByte(featureId));
onlineInfo.setXstatusName(info.getXstatusName());
onlineInfo.setXstatusDescription(info.getXstatusDescription());
}
if (mUserInterface != null) {
mUserInterface.onAccountStateChanged(info);
}
mStorage.saveAccount(account, true);
mNotificator.onAccountStateChanged(mAccounts);
} else {
Buddy b = account.getBuddyByProtocolUid(info.getProtocolUid());
if (b != null && b.getOnlineInfo() != info) {
b.getOnlineInfo().getFeatures().putByte(featureId, info.getFeatures().getByte(featureId));
}
if (mUserInterface != null) {
mUserInterface.onBuddyStateChanged(Arrays.asList(b));
}
}
}
};
private ICoreProtocolCallback.Stub mProtocolCallback = new ICoreProtocolCallback.Stub() {
@Override
public void typingNotification(byte serviceId, String ownerUid) throws RemoteException {
AccountService as = mAccounts.get(serviceId);
if (as == null || !as.getAccount().isEnabled() || as.getAccount().getConnectionState() != ConnectionState.CONNECTED) {
return;
}
Logger.log("Typing notification from: " + ownerUid, LoggerLevel.VERBOSE);
Account a = as.getAccount();
Buddy b = a.getBuddyByProtocolUid(ownerUid);
final Object icon;
final String params;
if (b != null) {
icon = ViewUtils.getIcon(getBaseContext(), b.getFilename());
params = b.getSafeName();
} else {
icon = new Object();
params = ownerUid;
}
mHandler.post(new Runnable() {
@Override
public void run() {
ViewUtils.showInformationToast(getBaseContext(), icon, R.string.logparam_typing, params);
}
});
}
@Override
public void message(Message message) throws RemoteException {
AccountService as = mAccounts.get(message.getServiceId());
if (as == null || !as.getAccount().isEnabled() || as.getAccount().getConnectionState() != ConnectionState.CONNECTED) {
return;
}
Logger.log("Protocol's message: " + message, LoggerLevel.VERBOSE);
Account a = as.getAccount();
while (a.getConnectionState() == ConnectionState.CONNECTING) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
}
onMessageInternal(message);
}
@Override
public void searchResult(byte serviceId, List<PersonalInfo> infoList) throws RemoteException {
AccountService as = mAccounts.get(serviceId);
if (as == null || !as.getAccount().isEnabled() || as.getAccount().getConnectionState() != ConnectionState.CONNECTED) {
return;
}
Logger.log("Search result list for #" + serviceId + ": ", LoggerLevel.VERBOSE);
if (mUserInterface != null) {
mUserInterface.onSearchResult(serviceId, infoList);
}
}
@Override
public void personalInfo(PersonalInfo info, boolean isShortInfo) throws RemoteException {
AccountService as = mAccounts.get(info.getServiceId());
if (as == null || !as.getAccount().isEnabled() || as.getAccount().getConnectionState() != ConnectionState.CONNECTED) {
return;
}
if (info.getProtocolUid().equals(as.getAccount().getProtocolUid())) {
OnlineInfo aou = as.getAccount().getOnlineInfo();
String name = info.getProperties().getString(PersonalInfo.INFO_NICK);
if (!TextUtils.isEmpty(name)) {
aou.setName(name);
if (mUserInterface != null) {
mUserInterface.onAccountStateChanged(aou);
}
}
} else {
// TODO use this somehow
}
if (!isShortInfo) {
if (mUserInterface != null) {
mUserInterface.onPersonalInfo(info);
}
}
}
@Override
public void notification(byte serviceId, final String message) throws RemoteException {
AccountService as = mAccounts.get(serviceId);
if (as == null || !as.getAccount().isEnabled()) {
return;
}
Account a = as.getAccount();
final Bitmap icon = ViewUtils.getIcon(getBaseContext(), a.getFilename());
mHandler.post(new Runnable() {
@Override
public void run() {
ViewUtils.showInformationToast(getBaseContext(), icon, R.string.simple_placeholder, message);
}
});
}
@Override
public void messageAck(byte serviceId, String ownerUid, long messageId, MessageAckState state) throws RemoteException {
AccountService as = mAccounts.get(serviceId);
if (as == null || !as.getAccount().isEnabled() || as.getAccount().getConnectionState() != ConnectionState.CONNECTED) {
return;
}
Logger.log("Protocol's message ack " + state + " for " + ownerUid, LoggerLevel.VERBOSE);
if (mUserInterface != null) {
mUserInterface.onMessageAck(serviceId, messageId, ownerUid, state);
}
}
@Override
public void iconBitmap(final byte serviceId, final String ownerUid, byte[] data, String hash) throws RemoteException {
AccountService as = mAccounts.get(serviceId);
if (as == null || !as.getAccount().isEnabled()) {
return;
}
Logger.log("Protocol's icon for " + ownerUid, LoggerLevel.VERBOSE);
Account a = as.getAccount();
if (a.getProtocolUid().equals(ownerUid)) {
ViewUtils.storeImageFile(getBaseContext(), data, a.getFilename(), hash, new Runnable() {
@Override
public void run() {
BitmapAjaxCallback.clearCache();
if (mUserInterface != null) {
try {
mUserInterface.onAccountIcon(serviceId);
} catch (RemoteException e) {
Logger.log(e);
}
}
}
});
} else {
Buddy b = a.getBuddyByProtocolUid(ownerUid);
if (b != null) {
ViewUtils.storeImageFile(getBaseContext(), data, b.getFilename(), hash, new Runnable() {
@Override
public void run() {
BitmapAjaxCallback.clearCache();
if (mUserInterface != null) {
try {
mUserInterface.onBuddyIcon(serviceId, ownerUid);
} catch (RemoteException e) {
Logger.log(e);
}
}
}
});
} else {
Logger.log("No target found for bitmap " + ownerUid);
}
}
}
@Override
public void groupAction(ItemAction action, final BuddyGroup newGroup) throws RemoteException {
AccountService as = mAccounts.get(newGroup.getServiceId());
if (as == null || !as.getAccount().isEnabled() || as.getAccount().getConnectionState() != ConnectionState.CONNECTED) {
return;
}
final Account account = as.getAccount();
BuddyGroup group = account.getBuddyGroupByGroupId(newGroup.getId());
int textId = 0;
switch (action) {
case DELETED:
if (group != null) {
account.getBuddyGroupList().remove(group);
textId = R.string.X_deleted;
}
break;
case ADDED:
if (group != null) {
Logger.log("Group with id #" + newGroup.getId() + " already exists", LoggerLevel.WARNING);
} else {
account.getBuddyGroupList().add(newGroup);
textId = R.string.X_added;
}
break;
case MODIFIED:
if (group != null) {
group.setName(newGroup.getName());
textId = R.string.X_modified;
} else {
Logger.log("Group with id #" + newGroup.getId() + " not found", LoggerLevel.WARNING);
}
break;
default:
Logger.log("Unsupported group action: " + action, LoggerLevel.INFO);
}
if (textId != 0) {
final int id = textId;
mHandler.post(new Runnable() {
@Override
public void run() {
ViewUtils.showInformationToast(getBaseContext(), ViewUtils.getIcon(getBaseContext(), account.getFilename()), id, newGroup.getName());
}
});
}
onContactListUpdated(account);
}
@Override
public void fileProgress(FileProgress fp) throws RemoteException {
AccountService as = mAccounts.get(fp.getServiceId());
if (as == null || !as.getAccount().isEnabled() || as.getAccount().getConnectionState() != ConnectionState.CONNECTED) {
return;
}
mNotificator.onFileTransferProgress(fp);
if (mUserInterface != null) {
mUserInterface.onFileProgress(fp);
}
}
@Override
public void connectionStateChanged(byte serviceId, ConnectionState connState, int extraParameter) throws RemoteException {
Logger.log("Protocol's connection state " + connState + " for account #" + serviceId, LoggerLevel.VERBOSE);
if (mExiting || serviceId >= mAccounts.size()) {
Logger.log("Service is not yet inited", LoggerLevel.INFO);
return;
}
AccountService as = mAccounts.get(serviceId);
if (as == null || !as.getAccount().isEnabled()) {
return;
}
final Account account = as.getAccount();
if (connState != ConnectionState.CONNECTED) {
for (Buddy b : account.getBuddyList()) {
ViewUtils.resetFeaturesForOffline(b.getOnlineInfo(), as.getProtocolService().getResources(false), true);
}
}
if (connState == ConnectionState.DISCONNECTED) {
if (account.getConnectionState() != ConnectionState.DISCONNECTED
&& account.getConnectionState() != ConnectionState.DISCONNECTING
&& extraParameter < 0) {
byte reconnectionAttempts = as.getConnectionAttempts();
if (reconnectionAttempts > 0) {
as.setConnectionAttempts((byte) (reconnectionAttempts - 1));
mScheduledExecutor.schedule(new ReconnectionTask(account.getServiceId()), 5, TimeUnit.SECONDS);
}
}
if (extraParameter > -1) {
final ProtocolException e = new ProtocolException(Cause.values()[extraParameter]);
mNotificator.processException(e, account);
}
}
account.setConnectionState(connState);
mNotificator.onAccountStateChanged(mAccounts);
if (connState == ConnectionState.CONNECTED) {
mStorage.saveServiceState(mAccounts);
as.setConnectionAttempts(sReconnectionAttempts);
as.resetConnectionTimeout();
} else if (connState == ConnectionState.CONNECTING) {
if (!as.isUnderConnectionMonitoring()) {
as.setConnectionTimeoutAction(mScheduledExecutor.schedule(new ConnectionTimeoutTask(as), 120, TimeUnit.SECONDS));
}
}
if (mUserInterface != null) {
mUserInterface.onConnectionStateChanged(serviceId, connState, extraParameter);
}
}
@Override
public void buddyStateChanged(List<OnlineInfo> infos) throws RemoteException {
if (infos == null || infos.size() < 1) {
return;
}
AccountService as = mAccounts.get(infos.get(0).getServiceId());
if (as == null || !as.getAccount().isEnabled() || as.getAccount().getConnectionState() != ConnectionState.CONNECTED) {
return;
}
Logger.log("Protocol's buddy state " + infos, LoggerLevel.VERBOSE);
boolean loadIcons = getBaseContext().getSharedPreferences(as.getAccount().getAccountId(), ServiceUtils.getAccessMode()).getBoolean(AccountOptionKeys.LOAD_ICONS.name(),
Boolean.parseBoolean(getBaseContext().getString(R.string.default_load_icons)));
List<Buddy> affectedBuddies = new ArrayList<Buddy>(infos.size());
for (OnlineInfo info : infos) {
Buddy buddy = as.getAccount().getBuddyByProtocolUid(info.getProtocolUid());
if (buddy == null) {
Logger.log("Cannot find buddy for online info #" + info.getProtocolUid(), LoggerLevel.WARNING);
} else {
buddy.getOnlineInfo().merge(info);
affectedBuddies.add(buddy);
if (loadIcons) {
checkIcon(buddy.getFilename(), buddy.getOnlineInfo(), as);
}
}
}
if (mUserInterface != null) {
mUserInterface.onBuddyStateChanged(affectedBuddies);
}
}
@Override
public void buddyListUpdated(byte serviceId, List<BuddyGroup> buddyList) throws RemoteException {
AccountService as = mAccounts.get(serviceId);
if (as == null || !as.getAccount().isEnabled()) {
return;
}
Logger.log("Protocol's buddy list updated for account #" + serviceId, LoggerLevel.VERBOSE);
boolean saveNotInList = getBaseContext().getSharedPreferences(as.getAccount().getAccountId(), ServiceUtils.getAccessMode()).getBoolean(AccountOptionKeys.SAVE_NOT_IN_LIST.name(),
Boolean.parseBoolean(getBaseContext().getString(R.string.default_save_not_in_list)));
as.getAccount().removeAllBuddies(saveNotInList);
as.getAccount().setBuddyList(buddyList);
onContactListUpdated(as.getAccount());
Executors.defaultThreadFactory().newThread(new IconChecker(as)).start();
}
@Override
public void buddyAction(ItemAction action, Buddy newBuddy) throws RemoteException {
AccountService as = mAccounts.get(newBuddy.getServiceId());
if (as == null || !as.getAccount().isEnabled() || as.getAccount().getConnectionState() != ConnectionState.CONNECTED) {
return;
}
Logger.log("Buddy #" + newBuddy + " with action: " + action, LoggerLevel.VERBOSE);
final Account account = as.getAccount();
Buddy oldBuddy = account.getBuddyByProtocolUid(newBuddy.getProtocolUid());
int textId = 0;
switch (action) {
case ADDED:
textId = R.string.X_added;
case MODIFIED:
if (textId == 0) {
textId = R.string.X_modified;
}
case JOINED:
if (oldBuddy != null) {
// account.removeBuddyByUid(newBuddy.getProtocolUid());
oldBuddy.merge(newBuddy);
newBuddy = oldBuddy;
} else {
account.addBuddyToList(newBuddy);
if (textId != 0) {
final int id = textId;
final String param1 = newBuddy.getSafeName();
mHandler.post(new Runnable() {
@Override
public void run() {
ViewUtils.showInformationToast(getBaseContext(), ViewUtils.getIcon(getBaseContext(), account.getFilename()), id, param1);
}
});
}
}
if (newBuddy instanceof MultiChatRoom) {
MultiChatRoom mcr = (MultiChatRoom) newBuddy;
for (BuddyGroup occupantsGroup : mcr.getOccupants()) {
for (Buddy occupant : occupantsGroup.getBuddyList()) {
if (occupant.getProtocolUid().equals(account.getProtocolUid())) {
occupant.getOnlineInfo().merge(account.getOnlineInfo());
} else {
Buddy b = account.getBuddyByProtocolUid(occupant.getProtocolUid());
if (b != null) {
occupant.merge(b);
}
}
}
}
}
if (mUserInterface != null) {
mUserInterface.onBuddyStateChanged(Arrays.asList(newBuddy));
}
break;
case LEFT:
if (oldBuddy != null) {
// account.removeBuddyByUid(newBuddy.getProtocolUid());
oldBuddy.merge(newBuddy);
newBuddy = oldBuddy;
newBuddy.getOnlineInfo().getFeatures().remove(ApiConstants.FEATURE_STATUS);
if (mUserInterface != null) {
mUserInterface.onBuddyStateChanged(Arrays.asList(newBuddy));
}
}
break;
case DELETED:
if (oldBuddy != null) {
BuddyGroup oldGroup = account.getBuddyGroupByGroupId(oldBuddy.getGroupId());
oldGroup.getBuddyList().remove(oldBuddy);
final String param2 = newBuddy.getSafeName();
mHandler.post(new Runnable() {
@Override
public void run() {
ViewUtils.showInformationToast(getBaseContext(), ViewUtils.getIcon(getBaseContext(), account.getFilename()), R.string.X_deleted, param2);
}
});
}
break;
}
onContactListUpdated(account);
}
@Override
public void accountStateChanged(OnlineInfo info) throws RemoteException {
if (info == null) {
return;
}
AccountService as = mAccounts.get(info.getServiceId());
if (as == null || !as.getAccount().isEnabled() || as.getAccount().getConnectionState() != ConnectionState.CONNECTED) {
return;
}
Logger.log("Protocol's account state " + info, LoggerLevel.VERBOSE);
Account account = as.getAccount();
account.setOwnName(info.getName());
account.getOnlineInfo().merge(info);
mUserInterface.onAccountUpdated(account, ItemAction.MODIFIED);
boolean loadIcons = getBaseContext().getSharedPreferences(as.getAccount().getAccountId(), ServiceUtils.getAccessMode()).getBoolean(AccountOptionKeys.LOAD_ICONS.name(),
Boolean.parseBoolean(getBaseContext().getString(R.string.default_load_icons)));
if (loadIcons) {
checkIcon(account.getFilename(), account.getOnlineInfo(), as);
}
}
@Override
public String requestPreference(byte serviceId, String preferenceName) throws RemoteException {
AccountService as = mAccounts.get(serviceId);
if (as == null || !as.getAccount().isEnabled()) {
return null;
}
Logger.log("Protocol requests value of " + preferenceName + " for account #" + serviceId, LoggerLevel.VERBOSE);
return mStorage.getProtocolOptionValue(preferenceName, as.getAccount());
}
@Override
public void accountActivity(byte serviceId, String text) throws RemoteException {
// TODO Auto-generated method stub
}
@Override
public void multiChatParticipants(byte serviceId, String chatUid, List<BuddyGroup> participantList) throws RemoteException {
AccountService as = mAccounts.get(serviceId);
if (as == null || !as.getAccount().isEnabled() || as.getAccount().getConnectionState() != ConnectionState.CONNECTED) {
return;
}
Buddy b = as.getAccount().getBuddyByProtocolUid(chatUid);
if (b == null || !(b instanceof MultiChatRoom)) {
Logger.log("Requested chat ID #" + chatUid + " does not belong to a chat!", LoggerLevel.INFO);
return;
}
MultiChatRoom c = (MultiChatRoom) b;
c.getOccupants().clear();
c.getOccupants().addAll(participantList);
if (mUserInterface != null) {
mUserInterface.onBuddyStateChanged(Arrays.asList(b));
}
}
@Override
public void showFeatureInputForm(byte serviceId, String uid, InputFormFeature feature) throws RemoteException {
AccountService as = mAccounts.get(serviceId);
if (as == null || !as.getAccount().isEnabled() || as.getAccount().getConnectionState() != ConnectionState.CONNECTED) {
return;
}
if (mUserInterface != null) {
mUserInterface.showFeatureInputForm(serviceId, uid, feature);
}
}
};
public void exitService(boolean terminate) {
Logger.log("Preparing exit", LoggerLevel.VERBOSE);
mExiting = true;
disconnectAllInternal(terminate);
cleanupResources();
if (!terminate) {
Executors.defaultThreadFactory().newThread(mSaveAccountsRunnable).start();
}
mServiceHelper.doStopForeground();
stopSelf();
}
private void respondMessageInternal(Message msg, boolean accept) {
AccountService as = mAccounts.get(msg.getServiceId());
if (as == null || !as.getAccount().isEnabled() || as.getAccount().getConnectionState() != ConnectionState.CONNECTED) {
return;
}
try {
if (msg instanceof FileMessage) {
if (!accept) {
FileProgress initFp = new FileProgress(msg.getServiceId(), msg.getMessageId(), ((FileMessage) msg).getFiles().get(0).getFilename(), 0, 0, true, as.getAccount().getProtocolUid(), null);
mNotificator.onFileTransferProgress(initFp);
if (mUserInterface != null) {
mUserInterface.onFileProgress(initFp);
}
} else {
mNotificator.removeFileNotification(msg.getMessageId());
}
}
as.getProtocolService().getProtocol().messageResponse(msg, accept);
} catch (RemoteException e) {
Logger.log(e);
}
}
private void disconnectInternal(byte serviceId) {
AccountService as = mAccounts.get(serviceId);
if (as == null || as.getAccount().getConnectionState() == ConnectionState.DISCONNECTED || as.getAccount().getConnectionState() == ConnectionState.DISCONNECTING) {
return;
}
try {
as.getAccount().setConnectionState(ConnectionState.DISCONNECTED);
as.resetConnectionTimeout();
as.getProtocolService().getProtocol().disconnect(serviceId);
if (mUserInterface != null) {
try {
mUserInterface.onConnectionStateChanged(as.getAccount().getServiceId(), as.getAccount().getConnectionState(), -1);
} catch (RemoteException e) {
Logger.log(e);
}
}
mStorage.saveServiceState(mAccounts);
} catch (Exception e) {
Logger.log(e);
}
}
private void connectInternal(byte serviceId) {
AccountService as = mAccounts.get(serviceId);
if (as == null || !as.getAccount().isEnabled() || as.getAccount().getConnectionState() != ConnectionState.DISCONNECTED) {
Logger.log("Trying to connect disabled or already connected account " + as.getAccount().getAccountId(), LoggerLevel.INFO);
return;
}
try {
as.getProtocolService().getProtocol().connect(as.getAccount().getOnlineInfo());
as.setConnectionTimeoutAction(mScheduledExecutor.schedule(new ConnectionTimeoutTask(as), 120, TimeUnit.SECONDS));
} catch (RemoteException e) {
Logger.log(e);
}
}
private void onContactListUpdated(Account account) throws RemoteException {
mStorage.saveAccount(account, false);
if (mUserInterface != null) {
mUserInterface.onContactListUpdated(account);
}
}
private void disconnectAllInternal(boolean doNotSave) {
for (byte i = 0; i < mAccounts.size(); i++) {
disconnectInternal(i);
}
if (!doNotSave) {
mStorage.saveServiceState(mAccounts);
}
}
private void cleanupResources() {
if (wifiLock != null && wifiLock.isHeld()) {
wifiLock.release();
wifiLock = null;
}
if (powerLock != null && powerLock.isHeld()) {
powerLock.release();
powerLock = null;
}
mNotificator.removeAppIcon();
for (AccountService as : mAccounts) {
if (as == null) {
continue;
}
mNotificator.removeAccountIcon(as.getAccount());
}
mProtocolServiceManager.onExit();
LocalBroadcastManager.getInstance(getBaseContext()).unregisterReceiver(mOptionsReceiver);
}
private void onMessageInternal(Message message) {
AccountService as = mAccounts.get(message.getServiceId());
Buddy buddy = as.getAccount().getBuddyByProtocolUid(message.getContactUid());
if (buddy == null) {
Logger.log("No buddy found for " + message, LoggerLevel.VERBOSE);
boolean noAuthFromAliens = getBaseContext().getSharedPreferences(as.getAccount().getAccountId(), ServiceUtils.getAccessMode()).getBoolean(AccountOptionKeys.DENY_MESSAGES_FROM_ALIENS.name(), false);
if (noAuthFromAliens) {
return;
}
buddy = buddyNotFromList(message, as);
}
mHistorySaver.saveMessage(buddy, message);
mNotificator.messageSound();
if (message instanceof FileMessage) {
mNotificator.onMessage(message, as.getAccount());
if (mUserInterface != null) {
try {
mUserInterface.onMessage(message);
} catch (RemoteException e) {
Logger.log(e);
}
}
} else {
if (mUserInterface != null) {
try {
mUserInterface.onMessage(message);
} catch (RemoteException e) {
Logger.log(e);
}
} else {
buddy.incrementUnread();
mNotificator.onMessage(message, as.getAccount());
}
}
if (buddy instanceof MultiChatRoom && buddy.getOnlineInfo().getFeatures().getByte(ApiConstants.FEATURE_STATUS, (byte) -1) < 0) {
boolean loadIcons = getBaseContext().getSharedPreferences(as.getAccount().getAccountId(), ServiceUtils.getAccessMode()).getBoolean(AccountOptionKeys.LOAD_ICONS.name(),
Boolean.parseBoolean(getBaseContext().getString(R.string.default_load_icons)));
try {
as.getProtocolService().getProtocol().joinChatRoom(message.getServiceId(), buddy.getProtocolUid(), loadIcons);
} catch (RemoteException e) {
Logger.log(e);
}
}
}
private Buddy buddyNotFromList(Message message, AccountService accountService) {
Logger.log("Creating alien buddy instance: " + message.getContactUid(), LoggerLevel.VERBOSE);
Account account = accountService.getAccount();
IProtocolService protocol = accountService.getProtocolService().getProtocol();
try {
String protocolName = protocol.getProtocolName();
final Buddy newBuddy = new Buddy(message.getContactUid(), account.getProtocolUid(), protocolName, message.getServiceId());
newBuddy.setGroupId(ApiConstants.NOT_IN_LIST_GROUP_ID);
account.addBuddyToList(newBuddy);
if (mUserInterface != null) {
mUserInterface.onContactListUpdated(account);
}
protocol.requestFullInfo(account.getServiceId(), newBuddy.getProtocolUid(), true);
return newBuddy;
} catch (RemoteException e) {
Logger.log(e);
}
return null;
}
private void checkIcon(String filename, OnlineInfo info, AccountService accountService) {
String hash = ViewUtils.getIconHash(getBaseContext(), filename);
if (TextUtils.isEmpty(hash) || !hash.equals(info.getIconHash())) {
try {
accountService.getProtocolService().getProtocol().requestIcon(info.getServiceId(), info.getProtocolUid());
} catch (Exception e) {
Logger.log(e);
}
}
}
private AccountService findAccountServiceByProtocolUidAndProtocolName(String protocolUid, String protocolName) {
for (AccountService as : mAccounts) {
if (as != null && as.getAccount().getProtocolUid().equals(protocolUid) && as.getAccount().getProtocolName().equals(protocolName)) {
return as;
}
}
return null;
}
public void sendLocation(Buddy buddy, String url) {
TextMessage message = new TextMessage(buddy.getServiceId(), buddy.getProtocolUid());
message.setIncoming(false);
message.setText(url);
message.setTime(System.currentTimeMillis());
try {
mInterfaceBinder.sendMessage(message);
if (mUserInterface != null) {
mUserInterface.onMessage(message);
}
} catch (RemoteException e) {
Logger.log(e);
}
}
public void importAccounts(List<Account> accounts) {
for (Account a : accounts) {
if (findAccountServiceByProtocolUidAndProtocolName(a.getProtocolUid(), a.getProtocolName()) != null) {
Logger.log("Will not import existing account #" + a.getProtocolUid(), LoggerLevel.INFO);
ViewUtils.showInformationToast(getBaseContext(), android.R.drawable.ic_menu_myplaces, R.string.simple_placeholder, a.getProtocolUid());
} else {
mAccounts.add(initAccount(a));
}
}
mStorage.saveServiceState(mAccounts);
;
}
private final class ReconnectionTask implements Runnable {
private final byte serviceId;
private ReconnectionTask(byte serviceId) {
this.serviceId = serviceId;
}
@Override
public void run() {
connectInternal(serviceId);
}
}
private final class ConnectionTimeoutTask implements Runnable {
private final AccountService mAccountService;
private ConnectionTimeoutTask(AccountService as) {
this.mAccountService = as;
}
@Override
public void run() {
try {
if (mAccountService.getAccount().getConnectionState() != ConnectionState.DISCONNECTED) {
mAccountService.getProtocolService().getProtocol().disconnect(mAccountService.getAccount().getServiceId());
}
connectInternal(mAccountService.getAccount().getServiceId());
} catch (RemoteException e) {
Logger.log(e);
}
}
}
private final class IconChecker implements Runnable {
private final AccountService accountService;
IconChecker(AccountService accountService) {
this.accountService = accountService;
}
@Override
public void run() {
boolean loadIcons = getBaseContext().getSharedPreferences(accountService.getAccount().getAccountId(), ServiceUtils.getAccessMode()).getBoolean(AccountOptionKeys.LOAD_ICONS.name(),
Boolean.parseBoolean(getBaseContext().getString(R.string.default_load_icons)));
if (loadIcons) {
for (Buddy b : accountService.getAccount().getBuddyList()) {
checkIcon(b.getFilename(), b.getOnlineInfo(), accountService);
}
}
}
}
}